<?php

require_once 'Basic.php';

class ObligartoryFieldNotGivenException extends Exception {
  public function __construct($message = "", $code = 0, $previous = null) {
    parent::__construct($message, $code, $previous);
  }
}

class OperationNotSupportedException extends Exception {
  public function __construct($message = "", $code = 0, $previous = null) {
    parent::__construct($message, $code, $previous);
  }
}

class ActionNotImplementedException extends Exception {
  public function __construct($message = "", $code = 0, $previous = null) {
    parent::__construct($message, $code, $previous);
  }
}

abstract class BasicEntity {
  
  protected $fields;
  
  private $optionalFields;
  private $requiredFields;
  
  protected function processFields() {
    $newFields = array();
    $this->optionalFields = array();
    $this->requiredFields = array();
    $this->fieldsNames = array();
    foreach($this->fields as $field) {
      $name = $field->name();
      $this->fieldsNames[] = $name;
      $newFields[$name] = $field;
      if($field->required()) {
        $this->requiredFields[] = $name;
      }
      else {
        $this->optionalFields[] = $name;
      }  
    }
    $this->fields = $newFields;
  }
  
  /**
   * 
   * @param $_GET/$_POST $userInput Os campos que devem ser usados como entrada à edição. Eles serão validado nessa
   * função.
   * @return Boolean/Array/String Se a edição foi bem sucedida, o valor de retorno é true. Caso contrário, será 
   * retornado um array de strings ou uma string (com mensagens de erros).
   */
  public function request($userInput) {
    $internalErrors = array();
    $userError = array();
    
    foreach($this->requiredFields as $required) {
      if(!isset($userInput[$required])) {
        $this->fields[$required]->setValid(false);
        $this->fields[$required]->setValue('');
        $internalErrors[] = "N&atilde;o foi passado nenhum " . $required . " para " . get_class($this) . ".";
      }
      else {
        $field = $this->fields[$required];
        $field->setValue(trim($userInput[$required]));
        $label = $field->label();
        if($field->value() === "") {
          $userError[] = "<span class='label'>${label}</span>" . ": campo vazio.";
          $field->setValid(false);
        }
        else {
          $err = $field->validate();
          if($err !== true) {
            $userError[] = "<span class='label'>${label}</span>" . ": " . $err;
            $field->setValid(false);
          }
          else {
            $field->setValid(true);
          }
        }
      }
    }
    
    if(count($internalErrors) != 0) {
      throw new ObligartoryFieldNotGivenException("Erro Interno: " . implode(" ", $internalErrors));
    }
    
    foreach($this->optionalFields as $optional) {
      if(isset($userInput[$optional])) {
        $val = trim($userInput[$optional]);
        $field = $this->fields[$optional];
        $field->setValue($val);
        if($val !== "") {
          $err = $field->validate();
          if($err !== true) {
            $label = $field->label();
            $userError[] = "<span class='label'>${label}</span>" . ": " . $err;
            $field->setValid(false);
          }
        }
        else {
          $field->setValue(null);
          $field->setValid(true);
        }
      }
      else {
        $this->fields[$optional]->setValid(false);
        $this->fields[$optional]->setValue(null);
        $internalErrors[] = "N&atilde;o foi passado nenhum " . $optional . " para " . get_class($this) . ".";
      }
    }
    
    if(count($userError) !== 0) {
      return $userError;
    }
    else {
      return $this->doAction($userInput);
    }
  }
  
  public function fields() {
    return $this->fields;
  }
  
  private function doAction($userInput) {
    $actionDescription = $this->actionDescription();
    $method =  "action" . $actionDescription;
    if(method_exists($this, $method)) {
      return call_user_func(array($this, $method), $userInput);
    }
    else {
      throw new ActionNotImplementedException($actionDescription);
    }
  }
  
  protected abstract function actionDescription();
}
